﻿using System;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Library.WebControls.Ds;
using System.Linq;

namespace Library.WebControls.SimpleCombobox {
	/// <summary>
	/// 
	/// </summary>
	[ToolboxBitmap(typeof(SpCombobox), "Library.WebControls.SimpleCombobox.SpCombobox.bmp")]
	[Designer(typeof(SpComboboxDesign))]
	[SupportsEventValidation]

	[ToolboxData("<{0}:SpCombobox runat=server></{0}:SpCombobox>")]

	[DesignTimeVisible(true)]
	public class SpCombobox : BaseAjaxControl, IEffectControl {
		public SpCombobox() {
			ClientScript = new ClientScript();
			AllValue = String.Empty;
			AllText = String.Empty;
			_dontShowIfEmplty = true;
			CloseMode = MenuContainerMode.ButtonAndText;
			ShowKey = SpShowKey.None;
		}
		internal const string CssFile = "spexcbx-ui.css";
		protected internal static Type SpComboboxType = typeof(SpCombobox);
		protected const string SpExComboboxControlCallbackParameter = "__SPEXCOMBOBOXRQP_";
		protected const string SpExComboboxControlCallbaclData = "__SPEXCOMBOBOXRQD_";
		protected const string SpExComboboxControlCallbackServerId = "__SPEXCOMBOBOXRQSID_";
		protected const string SpExComboboxControlCallbackFunction = "__SPEXCOMBOBOXRQF_";
		protected internal const string ComboboxCss = "spExCombobox";
		protected internal const string InputImagePanelCss = "excbInputPanel";
		protected internal const string InputImagePanelDisableCss = "excbInputPanelDisable";
		protected internal const string InputPanelCss = "excbInput";
		protected internal const string ContextMenuCss = "excbContextMenu";

		protected const string CustomCallbackFunction = "C";
		protected const string FilterCallbackFunction = "F";
		protected string CallbackFunction;
		public static bool IsSpExComboboxCallback(Page page) {
			return page.Request[SpExComboboxControlCallbackParameter] == "T";
		}

		internal void AddTextControl(string text) {
			var literal = new LiteralControl(text);
			AddParsedSubObject(literal);
		}


		protected override void OnInit(EventArgs e) {
			DataSource = Items;
			base.OnInit(e);

			//ItemBind();


			if (Request != null) {
				_isCallback = IsSpExComboboxCallback(Page) && Request[SpExComboboxControlCallbackServerId] == _divContextMenu.ClientID;
				if (IsCallback) {
					CallbackFunction = Request[SpExComboboxControlCallbackFunction];
				}
			} else {
				_isCallback = false;
			}
		}
		private bool _isCallback;
		public override bool IsCallback {
			get {
				return _isCallback;
			}
		}

		private bool _dontShowIfEmplty;
		[DefaultValue(true)]
		public virtual bool DontShowIfEmplty { get { return _dontShowIfEmplty; } set { _dontShowIfEmplty = value; } }
		protected override string StyleSheetFileName {
			get {

				return CssFile;
			}
		}

		public override bool ClientEnable {
			get {
				return base.ClientEnable;
			}
			set {
				base.ClientEnable = value;
				if (value) {
					_divTextButtonBox.Attributes["class"] = InputImagePanelCss;
				} else {

					_divTextButtonBox.Attributes["class"] = InputImagePanelDisableCss;
				}
			}
		}
		[DefaultValue(SpShowKey.None)]
		public SpShowKey ShowKey { get; set; }

		[PersistenceMode((PersistenceMode.InnerProperty)), Browsable(false), TemplateContainer(typeof(SpComboboxItemTemplate))]
		public ITemplate ItemTemplate { get; set; }

		/// <devdoc>
		///    <para>
		///       Indicates the collection of items within the list. 
		///       This property
		///       is read-only.</para> 
		/// </devdoc> 

		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]

		[NotifyParentProperty(true)]
		public virtual SpItemCollection Items {
			get {
				if (_items == null) {
					_items = new SpItemCollection();


				}
				return _items;
			}
		}
		private SpItemCollection _items;
		internal object GetTextObject(object value) {
			if (DataSource == null) return null;
			var items = DataSource as SpItemCollection;
			if (items != null) {
				var i = items.FirstOrDefault(it => it.Value.Equals(value));
				if (i != null) return i.Text;
			} else {
				var ds = new DataSourceWrapper(DataSource);
				while (ds.MoveNext()) {
					var fieldValue = ds.Current.GetPropertyValue(ValueField);
					if (fieldValue != null && fieldValue.Equals(value)) {
						object result = ds.Current.GetPropertyValue(TextField);
						return result;
					}
				}
			}
			return null;
		}

		[DefaultValue("")]
		public string AllText { get; set; }
		[DefaultValue("")]
		public string AllValue { get; set; }
		[DefaultValue(false)]
		public bool IncludeAll { get; set; }
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public virtual object DataSource { get; set; }
		[DefaultValue("")]
		public string TextField { get; set; }
		[DefaultValue("")]
		public string ValueField { get; set; }
		[DefaultValue("")]
		public string NoItemMatchText { get; set; }
		private HtmlGenericControl _divCombobox;
		private HtmlGenericControl _divTextButtonBox;
		private HtmlGenericControl _divInput;
		private HtmlGenericControl _divContextMenu;
		private HtmlGenericControl _divDataMenu;
		private TextBox _textCombobox;
		private HiddenField _hfValue;
		private System.Web.UI.WebControls.Image _image;

		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

		public System.Web.UI.WebControls.Image ImageButton {
			get {
				EnsureChildControls();
				return _image;
			}
		}
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

		public TextBox TextEdit {
			get {
				EnsureChildControls();
				return _textCombobox;
			}
		}


		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(false)]
		public virtual ClientScript ClientScript {
			private set;
			get;
		}


		protected override void CreateChildControls() {
			_divCombobox = new HtmlGenericNaming("div");
			_divCombobox.Attributes["class"] = ComboboxCss;

			_divTextButtonBox = new HtmlGenericControl("div");
			_divTextButtonBox.Attributes["class"] = InputImagePanelCss;

			_divInput = new HtmlGenericControl("div");
			_divInput.Attributes["class"] = InputPanelCss;
			//_divInput.Attributes["onmousedown"] = "return false;";

			_divContextMenu = new HtmlGenericControl("div");
			_divContextMenu.Attributes["class"] = ContextMenuCss;
			_divContextMenu.Style.Add(HtmlTextWriterStyle.Visibility, "hidden");
			//_divContextMenu.Style.Add(HtmlTextWriterStyle.Display, "none");
			_divDataMenu = new HtmlGenericControl("div");
			_divDataMenu.Style.Add(HtmlTextWriterStyle.Display, "none");
			_divDataMenu.Visible = false;
			_textCombobox = new WebTextEdit();
			_textCombobox.AutoCompleteType = AutoCompleteType.None;

			_textCombobox.BackColor = Color.Transparent;
			//	_textCombobox.BorderWidth=new Unit(0);
			_hfValue = new HiddenField();
			_divInput.Controls.Add(_textCombobox);
			_divInput.Controls.Add(_hfValue);

			_textCombobox.AutoCompleteType = AutoCompleteType.None;
			//textCombobox.ID = "txt";
			_image = new System.Web.UI.WebControls.Image {

				Width = new Unit(16),
				Height = new Unit(16),
				ImageUrl = "~/style/simple/images/navigate_close.png"
			};
			Controls.Add(_divCombobox);
			_divCombobox.Controls.Add(_divTextButtonBox);
			_divCombobox.Controls.Add(_divContextMenu);
			_divCombobox.Controls.Add(_divDataMenu);

			_divTextButtonBox.Controls.Add(_divInput);
			_divTextButtonBox.Controls.Add(_image);
			//divTextButtonBox.Controls.Add(divButton);
			//divButton.Controls.Add(image);
		}
		private SpComboboxFilterMode _filterMode = SpComboboxFilterMode.None;
		[DefaultValue(SpComboboxFilterMode.None)]
		public SpComboboxFilterMode FilterMode {
			get {
				EnsureChildControls();
				return _filterMode;
			}
			set {
				EnsureChildControls();
				_filterMode = value;
				_divDataMenu.Visible = value == SpComboboxFilterMode.FromClient;
			}
		}
		public Unit Height {
			get {
				EnsureChildControls();
				return new Unit(_divContextMenu.Style[HtmlTextWriterStyle.Height]);
			}
			set {
				EnsureChildControls();
				_divContextMenu.Style[HtmlTextWriterStyle.Height] = value.ToString();
			}
		}
		public Unit Width {
			get {
				EnsureChildControls();
				return new Unit(_divTextButtonBox.Style[HtmlTextWriterStyle.Width]);
			}
			set {
				EnsureChildControls();
				_divTextButtonBox.Style[HtmlTextWriterStyle.Width] = value.ToString();
				if (value.Type != UnitType.Percentage)
					_divContextMenu.Style[HtmlTextWriterStyle.Width] = value.ToString();
				//_textCombobox.Width = new Unit(value.Value - _image.Width.Value - 2);
			}
		}
		public override string ID {
			get {
				EnsureChildControls();
				return _divCombobox.ID;
			}
			set {
				EnsureChildControls();
				_divCombobox.ID = value;
				_textCombobox.ID = value + "Txt";
				_divContextMenu.ID = value + "Menu";
				_divDataMenu.ID = value + "MenuData";
				_divTextButtonBox.ID = value + "Panel";
				_hfValue.ID = value + "Value";
			}
		}
		[DefaultValue("")]
		public String Text {
			get {
				EnsureChildControls();
				return _textCombobox.Text;
			}
			set {
				EnsureChildControls();
				_textCombobox.Text = value;
			}
		}
		protected int SelecteIndex = -1;
		[DefaultValue("")]
		public string Value {
			get {
				EnsureChildControls();
				return _hfValue.Value;
			}
			set {
				EnsureChildControls();
				_hfValue.Value = value;
				Text = value;
				SelecteIndex = -1;


				if (_divContextMenu.Controls.Count == 0) {// non call databinding method
					if (DataSource != null) {
						if (!String.IsNullOrEmpty(TextField) && !String.IsNullOrEmpty(ValueField)) {
							var match = false;
							var dsWrapper = new DataSourceWrapper(DataSource);
							while (dsWrapper.MoveNext()) {
								SelecteIndex++;

								if (dsWrapper.Current.GetPropertyValue(ValueField).ToString() == value) {
									Text = dsWrapper.Current.GetPropertyValue(TextField).ToString();
									match = true;
									break;
								}
							}
							if (!match) SelecteIndex = -1;
						}
					}
					for (int i = 0; i < _divContextMenu.Controls.Count; i++) {
						var ctr = (SpComboboxItemTemplate)_divContextMenu.Controls[i];
						ctr.IsSelected = i == SelecteIndex;
					}

				} else {
					for (int i = 0; i < _divContextMenu.Controls.Count; i++) {
						var ctr = (SpComboboxItemTemplate)_divContextMenu.Controls[i];
						ctr.IsSelected = ctr.Value == value;
						if (ctr.IsSelected) {
							Text = ctr.Text;
							SelecteIndex = i;
						}
					}
				}
			}
		}

		public override string ClientID {
			get {
				EnsureChildControls();
				return _divCombobox.ClientID;
			}
		}
		protected override void OnLoad(EventArgs e) {
			base.OnLoad(e);
			IncludeScriptOnLoad(SpComboboxType, "Script", "Library.WebControls.SimpleCombobox.include.SpCombobox.js", UtilLib, EffectLib);

			if (IsCallback) {
				switch (CallbackFunction) {
					case CustomCallbackFunction:
						OnCustomCallback(new CustomCallbackEventArgs(GetClientQueryValue(SpExComboboxControlCallbaclData)));
						break;
					case FilterCallbackFunction:
						OnFilterCallback(new CustomCallbackEventArgs(GetClientQueryValue(SpExComboboxControlCallbaclData)));
						break;
				}
			}
		}

		protected virtual void OnCustomCallback(CustomCallbackEventArgs e) {
			var handler = (CustomCallbackEventHandler)Events[CustomCallbackEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}

		protected virtual void OnFilterCallback(CustomCallbackEventArgs e) {
			var handler = (CustomCallbackEventHandler)Events[FilterCallbackEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}
		private void ItemBind(int index) {
			//
			if (Items.Count == 0) return;

			//		var index = FirstBind();
			foreach (SpListItem listItem in Items) {
				var item = new SpComboboxItemTemplate();
				switch (FilterMode) {
					case SpComboboxFilterMode.None:
					case SpComboboxFilterMode.FromServer:
						_divContextMenu.Controls.Add(item);
						break;
					case SpComboboxFilterMode.FromClient:
						_divDataMenu.Controls.Add(item);
						break;
				}

				item.DataItem = listItem;
				item.DataItemIndex = index;
				item.DisplayIndex = index;
				item.Value = listItem.Value;
				item.Text = listItem.Text;
				item.IsSelected = listItem.Selected;

				//foreach (string key in listItem.Attributes.Keys) {
				//  item.Attributes.Add(key, listItem.Attributes[key]);
				//}
				if (ItemTemplate != null) {
					ItemTemplate.InstantiateIn(item);
				}

				var itemBoundHandler = (SimpleItemBoundEventHandler<SpComboboxItemTemplate>)Events[ItemBoundEvent];
				if (itemBoundHandler != null) {
					var ev = new SimpleItemBoundEventArgs<SpComboboxItemTemplate>(listItem, item);
					itemBoundHandler(this, ev);
					if (ev.Item.IsSelected) {
						_hfValue.Value = ev.Item.Value;
						Text = ev.Item.Text;
						SelecteIndex = item.DataItemIndex;
					}
				}

				if (FilterMode != SpComboboxFilterMode.FromClient)
					item.Attributes["onclick"] = ClientName + ".Call(6," + index + ")";
				index++;
			}
			base.DataBind();
		}
		private void DatasourceBind(int index) {
			if (DataSource is SpItemCollection) {
				ItemBind(index);
				return;
			}
			if (DataSource != null) {
				var dsWrapper = new DataSourceWrapper(DataSource);

				while (dsWrapper.MoveNext()) {
					var item = new SpComboboxItemTemplate();
					switch (FilterMode) {
						case SpComboboxFilterMode.None:
						case SpComboboxFilterMode.FromServer:
							_divContextMenu.Controls.Add(item);
							break;
						case SpComboboxFilterMode.FromClient:
							_divDataMenu.Controls.Add(item);
							break;
					}

					item.DataItem = dsWrapper.Current.OriginalItem;
					item.DataItemIndex = index;
					item.DisplayIndex = index;
					if (!String.IsNullOrEmpty(ValueField)) {
						item.Value = Convert.ToString(dsWrapper.Current.GetPropertyValue(ValueField));
					}
					if (!String.IsNullOrEmpty(TextField)) {
						item.Text = Convert.ToString(dsWrapper.Current.GetPropertyValue(TextField));
					}
					if (ItemTemplate != null) {
						ItemTemplate.InstantiateIn(item);
					}

					var itemBoundHandler = (SimpleItemBoundEventHandler<SpComboboxItemTemplate>)Events[ItemBoundEvent];
					if (itemBoundHandler != null) {
						var ev = new SimpleItemBoundEventArgs<SpComboboxItemTemplate>(dsWrapper.Current, item);
						itemBoundHandler(this, ev);
						if (ev.Item.IsSelected) {
							_hfValue.Value = ev.Item.Value;
							Text = ev.Item.Text;
							SelecteIndex = item.DataItemIndex;
						}
					}

					if (FilterMode != SpComboboxFilterMode.FromClient)
						item.Attributes["onclick"] = ClientName + ".Call(6," + index + ")";
					index++;
				}
			}
			base.DataBind();
		}

		private int FirstBind() {
			_divContextMenu.Controls.Clear();
			_divDataMenu.Controls.Clear();
			int index = 0;
			if (IncludeAll) {
				var item = new SpComboboxItemTemplate();
				switch (FilterMode) {
					case SpComboboxFilterMode.None:
					case SpComboboxFilterMode.FromServer:
						_divContextMenu.Controls.Add(item);
						break;
					case SpComboboxFilterMode.FromClient:
						_divDataMenu.Controls.Add(item);
						break;
				}
				item.DataItemIndex = -1;
				item.DisplayIndex = -1;
				item.Value = AllValue;
				item.Text = AllText;
				if (ItemTemplate != null) {
					ItemTemplate.InstantiateIn(item);
				}
				var itemBoundHandler = (SimpleItemBoundEventHandler<SpComboboxItemTemplate>)Events[ItemBoundEvent];
				if (itemBoundHandler != null) {
					var ev = new SimpleItemBoundEventArgs<SpComboboxItemTemplate>(item);
					itemBoundHandler(this, ev);
					if (ev.Item.IsSelected) {
						_hfValue.Value = ev.Item.Value;
						Text = ev.Item.Text;
						SelecteIndex = item.DataItemIndex;
					}
				}
				if (FilterMode != SpComboboxFilterMode.FromClient)
					item.Attributes["onclick"] = ClientName + ".Call(6," + index + ")";
				index++;
			}
			return index;
		}
		public override void DataBind() {
			var index = FirstBind();
			DatasourceBind(index);

		}
		[DefaultValue(true)]
		public override bool ClientVisible {
			get {
				EnsureChildControls();
				return _divTextButtonBox.Style[HtmlTextWriterStyle.Display] != "none";
			}
			set {
				EnsureChildControls();
				if (value) {
					_divTextButtonBox.Style[HtmlTextWriterStyle.Display] = "";
				} else {
					_divTextButtonBox.Style[HtmlTextWriterStyle.Display] = "none";
				}
			}
		}
		protected override void CreateClientObjectAtRender(HtmlTextWriter writer) {

			BeginScriptTag(writer);

			writer.Write("var {0}=new SpCombobox('{0}','{1}','{2}','{3}','{4}',{5},'{6}','{7}',{8},{9},'{10}','{11}',{12},{13},{14},{15}, {16});",
				ClientName,
				_divContextMenu.ClientID,
				_textCombobox.ClientID,
				_hfValue.ClientID,
				_divTextButtonBox.ClientID,
				(Int32)FilterMode,
				HttpContext.Current.Server.HtmlEncode(_textCombobox.Text),
				_hfValue.Value,
				SelecteIndex,
				500,
				_divDataMenu.ClientID,
				(Int32)DisplayEffect,
				TimeEffect,
				StepEffect,
				ClientEnable.ToString().ToLower(),
				DontShowIfEmplty.ToString().ToLower(),
				(Int32)ShowKey);
			if (!String.IsNullOrEmpty(NoItemMatchText)) {
				writer.Write("{0}.NoItemMatchText('{1}');", ClientName, HttpContext.Current.Server.HtmlEncode(NoItemMatchText));
			}
			AddScriptHandler(writer, 1, ClientScript.SelectIndexChanged, "AddHandler");
			AddScriptHandler(writer, 2, ClientScript.Shown, "AddHandler");
			AddScriptHandler(writer, 3, ClientScript.Showing, "AddHandler");
			AddScriptHandler(writer, 5, ClientScript.TextChanged, "AddHandler");

			EndScriptTag(writer);

		}
		[DefaultValue(MenuContainerMode.ButtonAndText)]
		public MenuContainerMode CloseMode { get; set; }
		protected override void Render(HtmlTextWriter writer) {
			if (!Visible) return;
			if (!TextEdit.Enabled || TextEdit.ReadOnly) {
				TextEdit.Style.Add(HtmlTextWriterStyle.Cursor, "default");
			}
			if (!_image.Visible) {
				_textCombobox.Width = new Unit(_divContextMenu.Style[HtmlTextWriterStyle.Width]);
			}
			if (CloseMode == MenuContainerMode.ButtonOnly || CloseMode == MenuContainerMode.ButtonAndText)
				_image.Attributes["onclick"] = ClientName + ".Call(2)";
			_divCombobox.Attributes["onkeyup"] = "return " + ClientName + ".Call(3, event)";
			_divCombobox.Attributes["onkeydown"] = "return " + ClientName + ".Call(4, event)";
			_divCombobox.Attributes["onkeypress"] = "return " + ClientName + ".Call(5, event)";
			_textCombobox.Attributes["onchange"] = ClientName + ".Call(1)";
			if (CloseMode == MenuContainerMode.ButtonAndText)
				_textCombobox.Attributes["onclick"] = ClientName + ".Call(2)";
			_textCombobox.Attributes["autocomplete"] = "off";

			if (IsCallback) {
				WriteCustomCallbackCompleteResult();
				if (FilterMode == SpComboboxFilterMode.FromClient) {
					foreach (Control ctr in _divDataMenu.Controls) {
						ctr.RenderControl(writer);
					}
				} else {
					foreach (Control ctr in _divContextMenu.Controls) {
						ctr.RenderControl(writer);
					}
				}
				FlushAndEndCallback();
			} else {
				base.Render(writer);
			}

		}
		public event SimpleItemBoundEventHandler<SpComboboxItemTemplate> ItemBound {
			add {
				Events.AddHandler(ItemBoundEvent, value);
			}
			remove {
				Events.RemoveHandler(ItemBoundEvent, value);
			}
		}
		public event CustomCallbackEventHandler CustomCallback {
			add {
				Events.AddHandler(CustomCallbackEvent, value);
			}
			remove {
				Events.RemoveHandler(CustomCallbackEvent, value);
			}
		}
		public event CustomCallbackEventHandler FilterCallback {
			add {
				Events.AddHandler(FilterCallbackEvent, value);
			}
			remove {
				Events.RemoveHandler(FilterCallbackEvent, value);
			}
		}
		private static readonly object ItemBoundEvent = new object();
		private static readonly object CustomCallbackEvent = new object();
		private static readonly object FilterCallbackEvent = new object();

		public Effect DisplayEffect { get; set; }
		public int TimeEffect { get; set; }
		public int StepEffect { get; set; }
	}
}
